package com.yanmaohu.nikita.parser;

import com.yanmaohu.nikita.NikitaException;
import com.yanmaohu.nikita.data.expression.*;
import com.yanmaohu.nikita.data.function.ArgumentList;
import com.yanmaohu.nikita.data.function.FunctionBody;
import com.yanmaohu.nikita.data.function.ParameterList;
import com.yanmaohu.nikita.data.operator.ArithmeticOperator;
import com.yanmaohu.nikita.data.operator.AssignmentOperator;
import com.yanmaohu.nikita.data.operator.BitOperator;
import com.yanmaohu.nikita.data.operator.BooleanOperator;
import com.yanmaohu.nikita.data.others.ArrayElementList;
import com.yanmaohu.nikita.data.others.ArrayIndex;
import com.yanmaohu.nikita.data.others.ArrayIndexList;
import com.yanmaohu.nikita.data.others.Sign;
import com.yanmaohu.nikita.data.structure.StructureInstance;
import com.yanmaohu.nikita.scanner.SourceCode;
import com.yanmaohu.nikita.scanner.Token;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Bright Lee
 */
public final class ExpressionParser {

    private Parser parser;
    private StatementParser statementParser;

    protected ExpressionParser(Parser parser) {
        this.parser = parser;
    }

    public void setStatementParser(StatementParser statementParser) {
        this.statementParser = statementParser;
    }

    private SourceCode getCode() {
        return parser.getCode();
    }

    protected Expression parseExpression() {
        Expression expression = tryParseExpression();
        if (expression != null) {
            return expression;
        }
        int markedIndex = mark();
        Token token = scan();
        reset(markedIndex);
        throw new NikitaException(getCode(), token, "This must be an expression.");
    }

    protected Expression tryParseExpression() {
        Expression expression = tryParseFirstAtomicExpression();
        if (expression == null) {
            return null;
        }
        while (true) {
            int markedIndex = mark();
            Token token = scan();
            if (token.isEof()) {
                return expression;
            }
            if (token.isEq() || token.isNe()) {
                BooleanOperator operator;
                if (token.isEq()) {
                    operator = BooleanOperator.EQ;
                } else {
                    operator = BooleanOperator.NE;
                }
                Expression b = parseAtomicExpression();
                expression = new BooleanExpression(expression.getBeginIndex(), b.getEndIndex(), operator, expression, b);
                continue;
            }
            if (token.isQuest()) {
                Expression b = parseAtomicExpression();
                token = scan();
                if (!token.isColon()) {
                    throw new NikitaException(getCode(), token, "This must be a :.");
                }
                Expression c = parseAtomicExpression();
                expression = new TernaryExpression(expression.getBeginIndex(), c.getEndIndex(), expression, b, c);
                continue;
            }
            if (token.isAnd()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new BooleanExpression(a.getBeginIndex(), b.getEndIndex(), BooleanOperator.AND, a, b);
                continue;
            }
            if (token.isAmRightShift()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new BitExpression(a.getBeginIndex(), b.getEndIndex(), BitOperator.ARITHMETIC_RIGHT_SHIFT, a, b);
                continue;
            }
            if (token.isRightShift()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new BitExpression(a.getBeginIndex(), b.getEndIndex(), BitOperator.RIGHT_SHIFT, a, b);
                continue;
            }
            if (token.isLeftShift()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new BitExpression(a.getBeginIndex(), b.getEndIndex(), BitOperator.LEFT_SHIFT, a, b);
                continue;
            }
            if (token.isBitAnd()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new BitExpression(a.getBeginIndex(), b.getEndIndex(), BitOperator.AND, a, b);
                continue;
            }
            if (token.isBitOr()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new BitExpression(a.getBeginIndex(), b.getEndIndex(), BitOperator.OR, a, b);
                continue;
            }
            if (token.isDiv()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new ArithmeticExpression(a.getBeginIndex(), b.getEndIndex(), ArithmeticOperator.DIV, a, b);
                continue;
            }
            if (token.isEq()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new BooleanExpression(a.getBeginIndex(), b.getEndIndex(), BooleanOperator.EQ, a, b);
                continue;
            }
            if (token.isExp()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new ArithmeticExpression(a.getBeginIndex(), b.getEndIndex(), ArithmeticOperator.EXP, a, b);
                continue;
            }
            if (token.isGt()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new BooleanExpression(a.getBeginIndex(), b.getEndIndex(), BooleanOperator.GT, a, b);
                continue;
            }
            if (token.isGe()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new BooleanExpression(a.getBeginIndex(), b.getEndIndex(), BooleanOperator.GE, a, b);
                continue;
            }
            if (token.isLt()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new BooleanExpression(a.getBeginIndex(), b.getEndIndex(), BooleanOperator.LT, a, b);
                continue;
            }
            if (token.isLe()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new BooleanExpression(a.getBeginIndex(), b.getEndIndex(), BooleanOperator.LE, a, b);
                continue;
            }
            if (token.isMinus()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new ArithmeticExpression(a.getBeginIndex(), b.getEndIndex(), ArithmeticOperator.SUB, a, b);
                continue;
            }
            if (token.isMul()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new ArithmeticExpression(a.getBeginIndex(), b.getEndIndex(), ArithmeticOperator.MUL, a, b);
                continue;
            }
            if (token.isNe()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new BooleanExpression(a.getBeginIndex(), b.getEndIndex(), BooleanOperator.NE, a, b);
                continue;
            }
            if (token.isOr()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new BooleanExpression(a.getBeginIndex(), b.getEndIndex(), BooleanOperator.OR, a, b);
                continue;
            }
            if (token.isPeriod()) {
                List<NameExpression> memberList = new ArrayList<>();
                while (true) {
                    NameExpression member = parseNameExpression();
                    memberList.add(member);
                    mark();
                    token = scan();
                    if (token.isPeriod()) {
                        continue;
                    }
                    reset();
                    break;
                }
                int beginIndex = expression.getBeginIndex();
                int endIndex = memberList.get(memberList.size() - 1).getEndIndex();
                expression = new MemberExpression(beginIndex, endIndex, expression, memberList);
                continue;
            }
            if (token.isLeftPar()) {
                reset(markedIndex);
                ArgumentList argumentList = parser.parseArgumentList();
                expression = new InvocationExpression(
                        expression.getBeginIndex(),
                        token.getEndIndex(),
                        expression,
                        argumentList);
                continue;
            }
            if (token.isPlus()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new ArithmeticExpression(expression.getBeginIndex(), b.getEndIndex(), ArithmeticOperator.ADD, a, b);
                continue;
            }
            if (token.isRem()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                expression = new ArithmeticExpression(a.getBeginIndex(), b.getEndIndex(), ArithmeticOperator.REM, a, b);
                continue;
            }
            if (token.isAddAssign()) {
                Expression a = expression;
                Expression b = parseExpression();
                expression = new AssignmentExpression(a.getBeginIndex(), b.getEndIndex(), AssignmentOperator.ADD_ASSIGN, a, b);
                continue;
            }
            if (token.isSubAssign()) {
                Expression a = expression;
                Expression b = parseExpression();
                expression = new AssignmentExpression(a.getBeginIndex(), b.getEndIndex(), AssignmentOperator.SUB_ASSIGN, a, b);
                continue;
            }
            if (token.isMulAssign()) {
                Expression a = expression;
                Expression b = parseExpression();
                expression = new AssignmentExpression(a.getBeginIndex(), b.getEndIndex(), AssignmentOperator.MUL_ASSIGN, a, b);
                continue;
            }
            if (token.isDivAssign()) {
                Expression a = expression;
                Expression b = parseExpression();
                expression = new AssignmentExpression(a.getBeginIndex(), b.getEndIndex(), AssignmentOperator.DIV_ASSIGN, a, b);
                continue;
            }
            if (token.isRemAssign()) {
                Expression a = expression;
                Expression b = parseExpression();
                expression = new AssignmentExpression(a.getBeginIndex(), b.getEndIndex(), AssignmentOperator.REM_ASSIGN, a, b);
                continue;
            }
            if (token.isExpAssign()) {
                Expression a = expression;
                Expression b = parseExpression();
                expression = new AssignmentExpression(a.getBeginIndex(), b.getEndIndex(), AssignmentOperator.EXP_ASSIGN, a, b);
                continue;
            }
            if (token.isAssign()) {
                Expression a = expression;
                Expression b = parseExpression();
                expression = new AssignmentExpression(a.getBeginIndex(), b.getEndIndex(), AssignmentOperator.ASSIGN, a, b);
                continue;
            }
            if (token.isBitAndAssign()) {
                Expression a = expression;
                Expression b = parseExpression();
                expression = new AssignmentExpression(a.getBeginIndex(), b.getEndIndex(), AssignmentOperator.BIT_AND_ASSIGN, a, b);
                continue;
            }
            if (token.isAndAssign()) {
                Expression a = expression;
                Expression b = parseExpression();
                expression = new AssignmentExpression(a.getBeginIndex(), b.getEndIndex(), AssignmentOperator.AND_ASSIGN, a, b);
                continue;
            }
            if (token.isBitOrAssign()) {
                Expression a = expression;
                Expression b = parseExpression();
                expression = new AssignmentExpression(a.getBeginIndex(), b.getEndIndex(), AssignmentOperator.BIT_OR_ASSIGN, a, b);
                continue;
            }
            if (token.isOrAssign()) {
                Expression a = expression;
                Expression b = parseExpression();
                expression = new AssignmentExpression(a.getBeginIndex(), b.getEndIndex(), AssignmentOperator.OR_ASSIGN, a, b);
                continue;
            }
            if (token.isIncAssign()) {
                expression = new AssignmentExpression(expression.getBeginIndex(), token.getEndIndex(), AssignmentOperator.SUF_INC, expression, null);
                continue;
            }
            if (token.isDecAssign()) {
                expression = new AssignmentExpression(expression.getBeginIndex(), token.getEndIndex(), AssignmentOperator.SUF_DEC, expression, null);
                continue;
            }
            if (token.isQuest()) {
                Expression a = expression;
                Expression b = parseAtomicExpression();
                token = scan();
                if (!token.isColon()) {
                    throw new NikitaException(getCode(), token.getBeginIndex(), token.getEndIndex(), "This must be a :.");
                }
                Expression c = parseAtomicExpression();
                expression = new TernaryExpression(a.getBeginIndex(), c.getEndIndex(), a, b, c);
                continue;
            }
            if (token.isTypeLimit()) {
                mark();
                token = scan();
                reset();
                NameExpression typeName = tryParseNameExpression();
                if (typeName == null) {
                    throw new NikitaException(getCode(), token, "This must be a type name.");
                }
                expression = new DataTypeLimitExpression(expression, typeName);
                continue;
            }
            if (token.isForceTypeConvert()) {
                mark();
                token = scan();
                reset();
                NameExpression typeName = tryParseNameExpression();
                if (typeName == null) {
                    throw new NikitaException(getCode(), token, "This must be a type name.");
                }
                expression = new DataTypeConvertExpression(expression, typeName);
                continue;
            }
            if (token.isLeftBrk()) {
                reset(markedIndex);
                ArrayIndexList arrayIndexList = parseArrayIndexList();
                if (arrayIndexList == null) {
                    throw new NikitaException(getCode(), token, "This must be a array index.");
                }
                expression = new ArrayElementReferenceExpression(
                        expression.getBeginIndex(),
                        arrayIndexList.getEndIndex(),
                        expression,
                        arrayIndexList);
                continue;
            }
            reset(markedIndex);
            break;
        }
        return expression;
    }

    private ArrayIndexList tryParseArrayIndexList() {
        int markedIndex = mark();
        Token token = scan();
        if (!token.isLeftBrk()) {
            reset(markedIndex);
            return null;
        }
        Expression expression = parseExpression();
        Token token1 = scan();
        if (!token1.isRightBrk()) {
            throw new NikitaException(getCode(), token1, "This must be a ].");
        }
        int endIndex = token1.getEndIndex();
        ArrayIndex arrayIndex = new ArrayIndex(token.getBeginIndex(), token1.getEndIndex(), expression);
        List<ArrayIndex> arrayIndexList = new ArrayList<>();
        arrayIndexList.add(arrayIndex);
        while (true) {
            int markedIndex3 = mark();
            Token token3 = scan();
            if (!token3.isLeftBrk()) {
                reset(markedIndex3);
                break;
            }
            Expression expression1 = parseExpression();
            final Token token4 = scan();
            if (!token4.isRightBrk()) {
                throw new NikitaException(getCode(), token4, "This must be a ].");
            }
            endIndex = token4.getEndIndex();
            arrayIndex = new ArrayIndex(token3.getBeginIndex(), token4.getEndIndex(), expression1);
            arrayIndexList.add(arrayIndex);
        }
        return new ArrayIndexList(token.getBeginIndex(), endIndex, arrayIndexList);
    }

    private ArrayIndexList parseArrayIndexList() {
        final int markedIndex = mark();
        final Token token = scan();
        reset(markedIndex);
        ArrayIndexList arrayIndexList = tryParseArrayIndexList();
        if (arrayIndexList == null) {
            throw new NikitaException(getCode(), token, "This must be a array index.");
        }
        return arrayIndexList;
    }

    private Expression tryParseFirstAtomicExpression() {
        final int markedIndex = mark();
        final Token token = scan();
        if (token.isNot()) {
            Expression a = parseAtomicExpression();
            return new BooleanExpression(token.getBeginIndex(), a.getEndIndex(), BooleanOperator.NOT, a);
        }
        if (token.isMinus() || token.isPlus()) {
            Sign sign;
            if (token.isMinus()) {
                sign = Sign.NEGATIVE;
            } else {
                sign = Sign.POSITIVE;
            }
            NumberExpression numberExpression = tryParseNumberExpression(token.getBeginIndex(), sign);
            if (numberExpression != null) {
                return numberExpression;
            }
            Expression a = parseAtomicExpression();
            return new SignedExpression(token.getBeginIndex(), a.getEndIndex(), sign, a);
        }
        if (token.isIntegerLiteral()) {
            reset(markedIndex);
            return parseNumberExpression(token.getBeginIndex(), null);
        }
        if (token.isBitInvert()) {
            Expression a = parseAtomicExpression();
            return new BitExpression(token.getBeginIndex(), a.getEndIndex(), BitOperator.INVERT, a);
        }
        if (token.isIncAssign() || token.isDecAssign()) {
            AssignmentOperator operator;
            if (token.isIncAssign()) {
                operator = AssignmentOperator.PRE_INC;
            } else {
                operator = AssignmentOperator.PRE_DEC;
            }
            Expression a = parseAtomicExpression();
            return new AssignmentExpression(token.getBeginIndex(), a.getEndIndex(), operator, a);
        }
        reset(markedIndex);
        TrueExpression trueExpression = tryParseTrueExpression();
        if (trueExpression != null) {
            return trueExpression;
        }
        FalseExpression falseExpression = tryParseFalseExpression();
        if (falseExpression != null) {
            return falseExpression;
        }
        CharacterExpression characterExpression = tryParseCharacterExpression();
        if (characterExpression != null) {
            return characterExpression;
        }
        StringExpression stringExpression = tryParseStringExpression();
        if (stringExpression != null) {
            return stringExpression;
        }
        StructureInstanceExpression structureInstanceExpression = tryParseStructureInstanceExpression();
        if (structureInstanceExpression != null) {
            return structureInstanceExpression;
        }
        NameExpression nameExpression = tryParseNameExpression();
        if (nameExpression != null) {
            return nameExpression;
        }
        ArrayInstanceExpression arrayInstanceExpression = tryParseArrayInstanceExpression();
        if (arrayInstanceExpression != null) {
            return arrayInstanceExpression;
        }
        FunctionExpression functionExpression = tryParseFunctionExpression();
        if (functionExpression != null) {
            return functionExpression;
        }
        ParenthesizedExpression parenthesizedExpression = tryParseParenthesizedExpression();
        if (parenthesizedExpression != null) {
            return parenthesizedExpression;
        }
        return null;
    }

    private NumberExpression parseNumberExpression(int beginIndex, Sign sign) {
        NumberExpression numberExpression = tryParseNumberExpression(beginIndex, sign);
        if (numberExpression != null) {
            return numberExpression;
        }
        Token token = scan();
        throw new NikitaException(getCode(), token, "Cannot resolve to number expression.");
    }

    private NumberExpression tryParseNumberExpression(int beginIndex, Sign sign) {
        Token unsignedInteger1 = tryParseIntegerLiteral();
        if (unsignedInteger1 == null) {
            return null;
        }
        Token unsignedInteger2 = null;
        Sign sign3 = null;
        Token unsignedInteger3 = null;
        int endIndex = unsignedInteger1.getEndIndex();
        int markedIndex1 = mark();
        Token token1 = scan();
        if (token1.isPeriod()) {
            unsignedInteger2 = parseIntegerLiteral();
            endIndex = unsignedInteger2.getEndIndex();
            markedIndex1 = mark();
            token1 = scan();
        }
        if (token1.isId() && "E".equalsIgnoreCase(token1.getContent())) {
            markedIndex1 = mark();
            token1 = scan();
            if (token1.isMinus()) {
                sign3 = Sign.NEGATIVE;
            } else if (token1.isPlus()) {
                sign3 = Sign.POSITIVE;
            } else {
                sign3 = null;
                reset(markedIndex1);
            }
            unsignedInteger3 = parseIntegerLiteral();
            endIndex = unsignedInteger3.getEndIndex();
        } else {
            reset(markedIndex1);
        }
        return new NumberExpression(
                beginIndex,
                endIndex,
                sign,
                unsignedInteger1.getContent(),
                unsignedInteger2 == null ? null : unsignedInteger2.getContent(),
                sign3,
                unsignedInteger3 == null ? null : unsignedInteger3.getContent());
    }

    private Token parseIntegerLiteral() {
        Token token = tryParseIntegerLiteral();
        if (token != null) {
            return token;
        }
        throw new NikitaException(getCode(), token, "Cannot resolve to integer literal.");
    }

    private Token tryParseIntegerLiteral() {
        int markedIndex = mark();
        Token token = scan();
        if (token.isIntegerLiteral()) {
            return token;
        }
        reset(markedIndex);
        return null;
    }

    private Expression tryParseAtomicExpression() {
        int markedIndex = mark();
        Token token = scan();
        if (token.isIntegerLiteral()) {
            return new NumberExpression(
                    token.getBeginIndex(),
                    token.getEndIndex(),
                    null,
                    token.getContent(),
                    null,
                    null,
                    null
                    );
        }
        reset(markedIndex);
        TrueExpression trueExpression = tryParseTrueExpression();
        if (trueExpression != null) {
            return trueExpression;
        }
        FalseExpression falseExpression = tryParseFalseExpression();
        if (falseExpression != null) {
            return falseExpression;
        }
        CharacterExpression characterExpression = tryParseCharacterExpression();
        if (characterExpression != null) {
            return characterExpression;
        }
        StringExpression stringExpression = tryParseStringExpression();
        if (stringExpression != null) {
            return stringExpression;
        }
        NameExpression nameExpression = tryParseNameExpression();
        if (nameExpression != null) {
            return nameExpression;
        }
        ParenthesizedExpression parenthesizedExpression = tryParseParenthesizedExpression();
        if (parenthesizedExpression != null) {
            return parenthesizedExpression;
        }
        return null;
    }

    private Expression parseAtomicExpression() {
        Expression expression = tryParseAtomicExpression();
        if (expression != null) {
            return expression;
        }
        final int markedIndex = mark();
        final Token token = scan();
        reset(markedIndex);
        throw new NikitaException(getCode(), token, "This must be a atomic expression.");
    }

    private ArrayInstanceExpression tryParseArrayInstanceExpression() {
        int markedIndex = mark();
        Token token = scan();
        if (!token.isLeftBrk()) {
            reset(markedIndex);
            return null;
        }
        Expression arrayLength = tryParseExpression();
        if (arrayLength == null) {
            Token token1 = scan();
            if (!token1.isRightBrk()) {
                throw new NikitaException(getCode(), token1, "This must be a ].");
            }
            return new ArrayInstanceExpression(token.getBeginIndex(), token1.getEndIndex(), null, null);
        }
        List<Expression> arrayElementList = new ArrayList<>();
        Token token1 = scan();
        if (token1.isSemicolon()) {
            Expression arrayElement = tryParseExpression();
            if (arrayElement != null) {
                arrayElementList.add(arrayElement);
            }
            token1 = scan();
        } else {
            arrayElementList.add(arrayLength);
            arrayLength = null;
        }
        while (true) {
            if (!token1.isComma()) {
                break;
            }
            Expression arrayElement = parseExpression();
            arrayElementList.add(arrayElement);
            token1 = scan();
        }
        if (!token1.isRightBrk()) {
            throw new NikitaException(getCode(), token1, "This must b a ].");
        }
        return new ArrayInstanceExpression(
                token.getBeginIndex(),
                token1.getEndIndex(),
                arrayLength,
                new ArrayElementList(arrayElementList)
        );
    }

    private TrueExpression tryParseTrueExpression() {
        final int markedIndex = mark();
        final Token token = scan();
        if (token.isTrue()) {
            return new TrueExpression(token);
        }
        reset(markedIndex);
        return null;
    }

    private FalseExpression tryParseFalseExpression() {
        final int markedIndex = mark();
        final Token token = scan();
        if (token.isFalse()) {
            return new FalseExpression(token);
        }
        reset(markedIndex);
        return null;
    }

    private CharacterExpression tryParseCharacterExpression() {
        final int markedIndex = mark();
        final Token token = scan();
        if (token.isCharacterLiteral()) {
            return new CharacterExpression(token);
        }
        reset(markedIndex);
        return null;
    }

    private StringExpression tryParseStringExpression() {
        final int markedIndex = mark();
        final Token token = scan();
        if (token.isStringLiteral()) {
            return new StringExpression(token);
        }
        reset(markedIndex);
        return null;
    }

    protected NameExpression tryParseNameExpression() {
        final int markedIndex = mark();
        final Token token = scan();
        if (!token.isId()) {
            reset(markedIndex);
            return null;
        }
        return new NameExpression(token);
    }

    protected NameExpression parseNameExpression() {
        final int markedIndex = mark();
        final Token token = scan();
        reset(markedIndex);
        NameExpression nameExpression = tryParseNameExpression();
        if (nameExpression == null) {
            throw new NikitaException(getCode(), token, "This must be a identifier.");
        }
        return nameExpression;
    }

    private StructureInstanceExpression tryParseStructureInstanceExpression() {
        StructureInstance structureInstance = parser.tryParseStructureInstance();
        if (structureInstance == null) {
            return null;
        }
        return new StructureInstanceExpression(structureInstance);
    }

    protected FunctionExpression tryParseFunctionExpression() {
        final int markedIndex = mark();
        ParameterList parameterList = parser.tryParseParameterList();
        if (parameterList == null) {
            return null;
        }
        final int markedIndex1 = mark();
        final Token token1 = scan();
        if (!token1.isLeftBrc()) {
            if (parameterList.size() > 1) {
                throw new NikitaException(getCode(), token1, "This must be a closure body.");
            }
            reset(markedIndex);
            return null;
        }
        reset(markedIndex1);
        FunctionBody functionBody = statementParser.parseStatementList();
        return new FunctionExpression(parameterList, functionBody);
    }

    private ParenthesizedExpression tryParseParenthesizedExpression() {
        final int markedIndex = mark();
        final Token token = scan();
        if (!token.isLeftPar()) {
            reset(markedIndex);
            return null;
        }
        Expression expression = tryParseExpression();
        if (expression == null) {
            reset(markedIndex);
            return null;
        }
        final Token token1 = scan();
        if (!token1.isRightPar()) {
            throw new NikitaException(getCode(), token1, "This must be a ).");
        }
        return new ParenthesizedExpression(token.getBeginIndex(), token1.getEndIndex(), expression);
    }

    int mark() {
        return parser.mark();
    }

    Token scan() {
        return parser.scan();
    }

    void reset(int markedIndex) {
        parser.reset(markedIndex);
    }

    void reset() {
        parser.reset();
    }

}
